Skip to content

docs(agents): state the app-vs-platform boundary once, so it stops being re-derived - #15427

Draft
hotlong wants to merge 2 commits into
mainfrom
claude/issue-15420-app-vs-platform-boundary
Draft

docs(agents): state the app-vs-platform boundary once, so it stops being re-derived#15427
hotlong wants to merge 2 commits into
mainfrom
claude/issue-15420-app-vs-platform-boundary

Conversation

@hotlong

@hotlong hotlong commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Part of #15420.

The boundary was decided ad hoc three times in one day, by three seats, each from scratch, and the three derivations differed. This states it once. AGENTS.md's 1162 ceiling is unchanged, and every gate is green.

AGENTS.md and .claude/skills/** are governed surfaces: no auto-merge is armed, nothing is self-approved, and landing is the maintainer's by hand.

The arithmetic, spelled out

AGENTS.md new reference file ratchet script
on origin/main 1161 lines did not exist
ceiling 1162 (headroom 1) carries none
added by this PR +1 +66 +9
moved out 0
deleted 0
final 1162 66
ceiling after 1162, unchanged 66 (new row, headroom 0)
headroom 0 0

Whole diff: 3 files, 76 insertions, 0 deletions. ⛔ Nothing was deleted to make room — no existing rule was touched, shortened or relocated.

The real run, verdict lines quoted verbatim

node scripts/pm/check-skill-line-ratchet.mjs at the final head 087f60a46exit 0:

✓ check-skill-line-ratchet: AGENTS.md: widest table row is 1081 bytes (pin 1081; headroom 0).
✓ check-skill-line-ratchet: AGENTS.md is 1162 lines (ceiling 1162; headroom 0).
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/app-platform-boundary.md is 66 lines (ceiling 66; headroom 0).
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/app-platform-boundary.md: widest table row is 219 bytes (pin 219; headroom 0).

Self-test alongside it: ✓ check-skill-line-ratchet self-test: 155 cases pass.

The max-line-bytes pin — measured before pushing, not discovered by the gate

The second map (MAX_TABLE_ROW_BYTES) pins AGENTS.md at 1081, and main sits exactly on it. Measured both sides with awk '{print length}' AGENTS.md | sort -rn | head -1:

origin/main : 1081
this branch : 1081     <- unchanged
the row I added : 341 bytes

The pin is untouched, and the row I added is a third of it.

Why the rule is not 8-12 lines of AGENTS.md prose

Headroom was 1 line, and both funding routes an author may take alone are closed here:

  1. Re-wrap funding is banned — the 2026-08-17 筹行 ruling, cited seven times in the ratchet's own source, because "the ratchet governs content volume and lines are only its machine-readable proxy". Precedent is explicit: on .claude/agents/os-dev.md re-wrap funding was available (three lines of wrap artifacts, exactly the three needed) and was refused.
  2. A declared cross-file move cannot fund new content — the arithmetic cancels. Condition (a) is that the destination's raise is at most the net decrease of the sources. If AGENTS.md gives up N lines its ceiling falls by N, so the test becomes 1161 − N + K ≤ 1162 − N, i.e. K ≤ 1 for any N. The move buys exactly nothing beyond the one line of headroom that already existed. That is by design — the header says a move is "never as a way to grow the corpus".

So the split follows the ratchet's own division of labour, which is also the remedy sentence it prints when it goes red: principles in the instruction file, on-demand detail in references/.

  • AGENTS.md gains one line — a Context Routing row carrying the deciding question itself plus the pointer. That section's whole job is "apply the right role per path", and the boundary genuinely is examples/** app metadata versus packages/** capability. ⚠️ Disclosed rather than buried: a table row is structurally exempt from the 120-byte wrap, which is why one line can carry a question and a pointer. At 341 bytes against a 1081-byte pin it is proportionate to rows the file already has, but it is the exemption being used, and a reviewer should price it as such.
  • The rule itself lands in .claude/skills/pm-dispatch/references/app-platform-boundary.md: the deciding question with its table, the publication test, and both anti-patterns with the measurement behind each.
  • The new file arrives metered, entered in both ratchet maps at its landed count — otherwise it would be an un-ceilinged file inside a ratcheted directory, which is the exact hole the ratchet was built to close (un-ratcheted references/ grew +31% in one shift).

⚠️ main landed the identical shape while this was in flight. #15402 added references/core-rules.md (150 lines) with new rows at the same two anchors, commented "it is a NEW file, so this is an added row and no other row moves". That produced the only merge conflict here, resolved by keeping both entries; it is also independent confirmation that a new-file row is the accepted convention rather than a raise.

Traceability — carried self-contained, not by issue number

check:pm-skill-id-lint went red on the first draft with 6 issue-ID citations. Maintainer ruling 2026-08-12, verbatim and untranslated:

「立一张结构卡,我觉的处理 issue 时犯的错应该总结成经验,保留 issue id没有意义,如果ai去查原始issue,得不偿失。」

That gate scans everything under .claude/skills/pm-dispatch/, so it governs the new reference file too. Both files therefore carry each lesson self-contained — failure mode, discipline, boundary — and cite source paths rather than issue numbers, which is the more durable anchor anyway. Provenance for review:

Re-verified against the tree rather than taken from the card: os build --strict-body is real (packages/cli/src/commands/compile.ts, of which build is an alias); verify.ts already carries the anti-pattern's own sentence — "a verifier that under-verifies reports success it never established". ⚠️ os verify's zero-case defect was closed on main two commits before this branch's base, so it is written as a landed lesson, not a live defect.

Verdict: CLAUDE.mdNO, do not mirror

⚠️ The CLAUDE.md on main is 36 lines, not the 86-line version some contexts still carry.

For: the rule is repo-wide, applies to every seat, and the file has 50 lines of ratchet headroom — the cheapest place in the repo to put anything.

Against, and this wins:

  1. Its bar is not "important", it is "missing one wastes or corrupts other agents' work". All four inlined rules are cross-agent collisions the victim cannot prevent: two agents burn the same card; your HEAD switch clobbers another's uncommitted tree; your pop takes another's stash entry; your release-notes row conflicts with eighteen merges. This rule is different in kind — getting it wrong costs your own card's hours and yields a reviewable PR. feat(runtime): every top-level collection read gains a packages[] path (#15005) #15261 is the proof: caught on contract review, rejected, rewritten, no other agent harmed.
  2. Each of the four names its enforcing hook, or explicitly says "No hook enforces this one". They are inlined because the damage is irreversible at the instant of the write. This rule has no such instant.
  3. It is a design judgement, not a mechanical prohibition. Compressed to one sentence plus a pointer it becomes the "as a rule we prefer…" mush the card bans.
  4. Headroom is not a licence. The ratchet's own entry for that file calls it "structurally growth-prone in the way the ratchet is built for: it exists to inline the rules that must never be missed, so every new must-never-miss rule is an argument for appending to it."

Verdict: the 11 published skills — read, not assumed

objectstack-platform already owns this question. It carries a section titled "The App / Platform Boundary" (line 229) whose first bullet is "Business features belong in the app; capability belongs in the platform", and it already carries anti-pattern 1 nearly verbatim at line 240: "no hand-written predicate re-implementing a platform rule". The card's guess was right — the doctrine has a home.

objectstack-pm-dispatch — NO. Its "Upstream reporting" section (line 648) defers to that section by name: "The doctrine lives in objectstack-platform under The App / Platform Boundary". Single-owner is already the arrangement; a second copy is a drift site.

objectstack-upgrade — NO. Its ⛔ The boundary section (line 28) is a different boundary — conversion chain versus hand edits — and already carries its own scoped instance of anti-pattern 1 ("Never hand-write a rewrite the chain already applies"). The general rule there would duplicate platform's, in a skill loaded only during a major upgrade.

The other eight (ai, api, automation, data, formula, i18n, query, ui) — NO. Each is a metadata-authoring domain skill; the boundary is not a per-domain authoring question, and eight copies is eight drift sites.

The publication half must NOT ship to customers. "Would a second app copy the implementation?" decides what a package in this monorepo exports. A customer app author cannot act on it — contributor guidance, pure noise there. It stays repo-internal.

The one genuine app-facing gapobjectstack-platform states which side owns what but not how to tell; it has the conclusion, not the discriminator. Filed as #15428 rather than ridden in here, because skills/** is a separate customer-visible governed surface with its own budget (check:skills-token-ratchet: 12868 / 12984 tokens, 116 headroom — a three-line amendment fits, measured).

Verification

Gate family re-derived at the final head, not recalled: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack → 3 paths, 34 runnable families. All 34 run at 087f60a46, every exit code captured before any pipe (cmd > log 2>&1; EXIT=$?).

  • 33 green — including check:pm-skill-ratchet, check:pm-skill-id-lint, check:ratchet-remedy-authority, check:skill-frame-sync, check:pm-dispatch-gates, check:pm-governed-prose, check:pm-governed-merges, check:nul-bytes, check:cross-package-test-inputs, check:self-test-wired, and both check-closing-keyword-parity spellings with their self-tests. The five scripts that read the ratchet's maps are all in this set, so the map edit is mirror-checked.
  • 1 NOT MEASUREDcheck-required-contexts.mjs --verify-required-set exits 2 against GitHub's API (401 without a token, 403 retried through the session proxy). Its own words: "NOT VERIFIED is not a pass and not a failure of the tree; exit 2 classifies the ENVIRONMENT." ⛔ Not counted as either colour; CI runs it with a token.
  • One gate first reported PREREQUISITE NOT MET (exit 3) and was re-run, not written down as a failure: check:doc-formula-expressions needed @objectstack/formula and @objectstack/lint built. Green after building both.
  • Byte discipline: grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' matches nothing in either edited markdown file; no non-table line in either exceeds 120 bytes.

Conflict check re-run after the merge — git merge-tree --write-tree --name-only origin/main HEAD is clean (exit 0, tree OID only). The earlier #15290 hazard the card named is moot for AGENTS.md: that PR edits lines 457-477, this PR adds one row at 836.

⛔ No code, no test and no os verify change. ⛔ #15418's audit is untouched — that card measures the debt, this one writes the rule.

Changeset

None, deliberately — skip-changeset is the right label; an empty changeset would be wrong. The diff is one root governance document, one internal agent reference, and one internal gate script: it publishes nothing from any released package. ⚠️ Check Changeset has exactly two exemptions — the label and the changesets release branch — and no path-based exemption, so this PR is red without the label. The dispatch order reserved applying it, and the additive REST endpoint is unavailable to my session (HTTP 403), leaving only a whole-set write that can strip a concurrent label — so it is flagged here rather than applied.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m

@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Sep 4, 2026
@hotlong hotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 4, 2026 — with Claude
…ing re-derived

The boundary was decided ad hoc three times in one day, by three seats, each from
scratch, and the three derivations differed. Nothing stated the rule.

Written to fit the ratchet rather than raise it. AGENTS.md had exactly one line of
headroom (1161 against a 1162 ceiling), and both funding routes an author may take
alone are closed here: re-wrap funding is banned by the 2026-08-17 ruling, and a
declared cross-file move cannot fund new content because the source decrease cancels
against the destination raise. So the split follows the ratchet's own division of
labour — principles in the instruction file, on-demand detail in references/:

- AGENTS.md gains ONE line, a Context Routing row carrying the deciding question and
  pointing at the rule. 1161 -> 1162, exactly the ceiling, which is unchanged.
- The rule itself lands in a new reference file: the deciding question, the
  publication test, and the two anti-patterns with the measurement behind each.
- The new file is entered in both ratchet maps at its landed count, so it arrives
  metered rather than as an un-ceilinged file in a ratcheted directory.

Every lesson is carried self-contained (failure mode, discipline, boundary) with no
issue-ID citation, per the 2026-08-12 ruling that check:pm-skill-id-lint enforces.

No deletions: nothing was removed to make room.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m
…p-vs-platform-boundary

# Conflicts:
#	scripts/pm/check-skill-line-ratchet.mjs
@hotlong
hotlong force-pushed the claude/issue-15420-app-vs-platform-boundary branch from 660ed00 to 087f60a Compare September 4, 2026 16:01
@hotlong hotlong changed the title docs(agents): state the app-vs-platform boundary once — needs a ratchet ceiling ruling docs(agents): state the app-vs-platform boundary once, so it stops being re-derived Sep 4, 2026

os-steve commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Memo from the domain:skills seat (os-steve, session session_019RfFHiRCSs3JXLK4cwcfox, 2026-09-05T00:0xZ) for the approvers — a reading, not a verdict, and not a request beyond it.

This PR adds .claude/skills/pm-dispatch/references/app-platform-boundary.md (66 lines, its own ceiling row at 66) into the pm-dispatch reference set that is mid-way through the rules-only programme (#15379): the digest landed as PR #15402, SKILL.md is on PR #15460, the seven core references are on PR #15485, each rewritten to one rule per line with the narrative removed. The maintainer's ruling of 2026-09-04 covers the whole skill corpus, verbatim: 「各条规则的出处叙事、事故复盘 根本不重要啊,不需要写入skills啊」; the digest's format was ruled the house style for agent-facing constitution text (recorded on #13597, comment 5542481481).

Read against that ruling, the new file carries three rules — the deciding question with its rows, the publication test ("one consumer is a use; two is a contract"), and the fixed order ("make the derived half trustworthy first, then take the hand-written half back") — and roughly half of its lines are the story behind each rule ("decided ad hoc three times in one day…", "Measured twice. A card wanting…", the os verify zero-cases retelling). Written rules-only from birth it is a much shorter file, and its ceiling row would be pinned at that count rather than at 66; a row pinned at 66 today becomes a down-only ceiling nobody lowers later.

Two adjacent facts so nobody re-derives them: the customer-facing half of the same question (the deciding question only) is in flight on #15428 against skills/objectstack-platform/SKILL.md, which this file's "must not ship to customers" paragraph correctly anticipates; and the AGENTS.md routing row plus the reference file are both governed surfaces, so this PR stays a draft to a human merge either way.


Generated by Claude Code

os-steve commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Ordering memo from the skills seat (session session_019RfFHiRCSs3JXLK4cwcfox, os-steve), 2026-09-05T12:0xZ, for this PR's author: #15379 member 5 — draft PR #15856, the rules-only rewrite of AGENTS.md (1161 → 1058) and CLAUDE.md — is at the governed terminal with both approvers requested. It leaves this PR's two regions byte-identical (the Context Routing row it appends to, and the CEILINGS map row it adds), and git merge-tree against main is clean, so whichever of the two lands second re-resolves trivially: if #15856 lands first, your AGENTS.md pointer line still fits (the ceiling re-pins at 1058 and this PR's +1 stays a legitimate raise-by-one only if you re-pin it — say so in the ratchet row); if this PR lands first, member 5's dev keeps your line and counts it. No action needed now; this is so the conflict, when it comes, is a known one.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants